using System;
using System.Threading;

namespace WindowsPhoneEssentials.Threading
{
    /// <summary>
    /// Simple background worker that posts result to the UI Thread
    /// </summary>
    public class BackgroundWorker : IBackgroundWorker
    {
        private readonly SynchronizationContext synchronizationContext;

        public BackgroundWorker()
        {
            synchronizationContext = SynchronizationContext.Current;
            if (synchronizationContext == null)
                throw new InvalidOperationException("Synchronisation context is null, ctor should be called from UI Thread");
        }

        public void DoWork<TResult>(Func<TResult> doWork, Action<BackgroundResult<TResult>> workCompleted)
        {
            ThreadPool.QueueUserWorkItem(o => DoWorkWithExceptionHandling(doWork, workCompleted));
        }

        public void DoWork(Action doWork, Action<BackgroundResult> workCompleted)
        {
            ThreadPool.QueueUserWorkItem(o => DoWorkWithExceptionHandling(doWork, workCompleted));
        }

        private void DoWorkWithExceptionHandling(Action doWork, Action<BackgroundResult> workCompleted)
        {
            Exception exception = null;

            try
            {
                doWork();
            }
            catch (Exception e)
            {
                exception = e;
            }
            finally
            {
                synchronizationContext.Post(
                    delegate
                    {
                        if (exception != null)
                        {
                            workCompleted(new BackgroundResult(exception));
                            return;
                        }

                        workCompleted(new BackgroundResult());
                    }, null);
            }
        }

        private void DoWorkWithExceptionHandling<TResult>(Func<TResult> doWork, Action<BackgroundResult<TResult>> workCompleted)
        {
            Exception exception = null;
            var result = default(TResult);

            try
            {
                result = doWork();
            }
            catch (Exception e)
            {
                exception = e;
            }
            finally
            {
                synchronizationContext.Post(
                    delegate
                    {
                        if (exception != null)
                        {
                            workCompleted(new BackgroundResult<TResult>(exception));
                            return;
                        }

                        workCompleted(new BackgroundResult<TResult>(result));
                    }, null);
            }
        }
    }
}